import React from 'react';
import RoutingContext from './RoutingContext';
//import ErrorBoundary from '../ErrorBoundary';
import { ErrorBoundary } from "react-error-boundary";
import './RouteRenderer.css';

//JS毛病：useTransition,没有定义的也不会告警，运行报错才知道的。
const { useContext, useEffect,  Suspense, useState } = React;

//const SUSPENSE_CONFIG = { timeoutMs: 2000 };

function  errTranslate(error){
  let dispaly;
  if(error.message === 'Failed to fetch')
    dispaly="后端没响应啊";
  else dispaly=error.message;
  return dispaly;
}

/**
 * A component that accesses the current route entry from RoutingContext and renders
 * that entry.
 */
export default function RouterRenderer() {
  // Access the router
  const router = useContext(RoutingContext);
  // Improve the route transition UX by delaying transitions: show the previous route entry
  // for a brief period while the next route is being prepared. See
  // https://reactjs.org/docs/concurrent-mode-patterns.html#transitions
//  const [startTransition, isPending] = useTransition(SUSPENSE_CONFIG);

  // Store the active entry in state - this allows the renderer to use features like
  // useTransition to delay when state changes become visible to the user.
  //? router.get()是旧的页面路由的？
  const [routeEntry, setRouteEntry] = useState(router.get());

  // On mount subscribe for route changes
  useEffect(() => {
    // Check if the route has changed between the last render and commit:
    const currentEntry = router.get();
    if (currentEntry !== routeEntry) {
      // if there was a concurrent modification, rerender and exit
      setRouteEntry(currentEntry);
      return;
    }
    //切换路由还有的搞
    // If there *wasn't* a concurrent change to the route, then the UI
    // is current: subscribe for subsequent route updates
    //真正？入口点
    const dispose = router.subscribe(nextEntry => {
      // startTransition() delays the effect of the setRouteEntry (setState) call
      // for a brief period, continuing to show the old state while the new
      // state (route) is prepared.
      //按钮延迟或 回调函数延迟执行。
      /*startTransition(() => {
        setRouteEntry(nextEntry);
      });
      */
      setRouteEntry(nextEntry);
    });
    return () => dispose();

    // Note: this hook updates routeEntry manually; we exclude that variable
    // from the hook deps to avoid recomputing the effect after each change
    // triggered by the effect itself.
    // eslint-disable-next-line
  }, [router]);

  // The current route value is an array of matching entries - one entry per
  // level of routes (to allow nested routes). We have to map each one to a
  // RouteComponent to allow suspending, and also pass its children correctly.
  // Conceptually, we want this structure:
  // 多层次的嵌套 路由表！
  // ```
  // <RouteComponent
  //   component={entry[0].component}
  //   prepared={entrry[0].prepared}>
  //   <RouteComponent
  //     component={entry[1].component}
  //     prepared={entry[1].prepared}>
  //       // continue for nested items...
  //   </RouteComponent>
  // </RouteComponent>
  // ```
  // To achieve this, we reverse the list so we can start at the bottom-most
  // component, and iteratively construct parent components w the previous
  // value as the child of the next one:
  const reversedItems = [].concat(routeEntry.entries).reverse(); // reverse is in place, but we want a copy so concat
  const firstItem = reversedItems[0];
  //顶级多个？
  if(!firstItem){
    console.log("顶级多个？reversedItems=", reversedItems);
    return null;
  }

  // the bottom-most component is special since it will have no children
  // (though we could probably just pass null children to it)
  let routeComponent = (
      <RouteComponent
        component={firstItem.component}
        prepared={firstItem.prepared}
        routeData={firstItem.routeData}
      />
  );
  //多层次 嵌套的 路由。
  for (let ii = 1; ii < reversedItems.length; ii++) {
    const nextItem = reversedItems[ii];
    //顶级多个？
    if(!nextItem)
      break;
    routeComponent = (
        <RouteComponent
          component={nextItem.component}
          prepared={nextItem.prepared}
          routeData={nextItem.routeData}
        >
          {routeComponent}
        </RouteComponent>
    );
  }

 // console.log("当前render路由页？reversedItems=",reversedItems.length,":", reversedItems);
  // Routes can error so wrap in an <ErrorBoundary>
  // Routes can suspend, so wrap in <Suspense>
  /**这底下routeComponent已经是嵌套路由后的结果
   * */
  return (
    <ErrorBoundary fallbackRender={({ error }) => <div>{errTranslate(error)}</div>}>
      <Suspense fallback={'Loading fallback...'}>
        {/* Indicate to the user that a transition is pending, even while showing the previous UI
          删除掉了startTransition; 效果不好。
        */}
        {routeComponent}
      </Suspense>
    </ErrorBoundary>
  );
}

/**
 * The `component` property from the route entry is a Resource, which may or may not be ready.
 * We use a helper child component to unwrap the resource with component.read(), and then
 * render it if its ready.
 *
 * NOTE: calling routeEntry.route.component.read() directly in RouteRenderer woldn't work the
 * way we'd expect. Because that method could throw - either suspending or on error - the error
 * would bubble up to the *caller* of RouteRenderer. We want the suspend/error to bubble up to
 * our ErrorBoundary/Suspense components, so we have to ensure that the suspend/error happens
 * in a child component.
 * 这底下 prepared 是graphql 预先获取的查询结果数据。
 * 真正路由 实际上用 RoutingContext 注入的。
 */
function RouteComponent(props) {
  //静态路由【】里面配置的props.component实际上只是相应组件的源代码，并不是React组件本尊，不能直接用来render()的。
  //假如路由表设置的props.component是通常意义的React.Component的情况，那么就没必要下面添加component.read();我这里都是源代码资源指代的。
  if(!props.component) {
        //  route.component.load(); //触发加载源代码
    return null;    //还未加载完成吧
  }

 // var _resource$read = props.component.read();
  //只能是单一个文件名的缺省输出的！
 // const  Component = _resource$read["default"];

  //const Component = props.component.read();
  const Component = props.component;

  //必须提前触发加载，否则上面这里props.component可能为空的。
  const { routeData, prepared } = props;
  //console.log("RouteComponent跑来Component=", Component,"matchData=",routeData);

  return (
    <Component
      routeData={routeData}
      prepared={prepared}
      children={props.children}
    />
  );
}

